package cjq.util;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;

import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.util.CollectionUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.net.URLEncoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by rwy-mark on 2017/5/18.
 */
public class HttpClientUtils {
    private static Log logger = LogFactory.getLog(HttpClientUtils.class);

    private static PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
    private static CloseableHttpClient httpClient = null;
    private static final int DEFAULT_MAX_TOTAL_CONNECTION = 1024;
    private static final int DEFAULT_MAX_PER_ROUTE = 300;
    private static final String DEFAULT_ENCODING = "UTF-8"; // 默认的查询参数及返回结果的字符串编码
    private static final String CONTENT_TYPE_TEXT_XML = "application/xml"; // 默认的查询参数及返回结果的字符串编码
    private static final String APPLICATION_XML = "text/xml"; // 默认的查询参数及返回结果的字符串编码
    private static final int DEFAULT_CONNECTION_TIME_OUT = 60000; // 默认连接超时时间,
    // 60秒
    private static final int DEFAULT_READ_TIME_OUT = 200000; // 默认响应超时时间, 60秒

    public static JSONObject custRespHeader = new JSONObject();

    static {
        poolingHttpClientConnectionManager.setMaxTotal(DEFAULT_MAX_TOTAL_CONNECTION);
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(DEFAULT_MAX_PER_ROUTE);
        httpClient = HttpClients.custom().setConnectionManager(poolingHttpClientConnectionManager).build();
    }

    private HttpClientUtils() {
    }

    /**
     * @param url
     * @param params
     * @param encode
     * @param connectTimeout
     * @param readTimeout
     * @return
     */
    public static String invokeGet(String url, Map<String, String> params, String encode, int connectTimeout,
                                   int readTimeout) {
        if (connectTimeout <= 0) {
            return null;
        }
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeout)
                .setConnectTimeout(connectTimeout).setConnectionRequestTimeout(connectTimeout).build();
        String responseString;
        String requestUrl;
        try {
            requestUrl = buildRequestUrl(url, params);
        } catch (UnsupportedEncodingException e) {
            logger.error("encode http get params error, params is " + params, e);
            return "";
        }
        HttpPost httpGet = new HttpPost(requestUrl);
        httpGet.setHeader("Connection", "close");
        httpGet.setConfig(requestConfig);
        responseString = doRequest(httpGet, encode);
        return responseString;
    }

    /**
     * @param requestUrl
     * @param params
     * @return
     */
    public static String invokePost(String requestUrl, Map<String, Object> params) {
        return invokePost(requestUrl, params, null, null, DEFAULT_ENCODING, DEFAULT_CONNECTION_TIME_OUT,
                DEFAULT_READ_TIME_OUT);
    }
    public static String invokePost(String requestUrl, Map<String, Object> params,Map<String, String> requestHeader) {
        return invokePost(requestUrl, params, requestHeader, null, DEFAULT_ENCODING, DEFAULT_CONNECTION_TIME_OUT,
                DEFAULT_READ_TIME_OUT);
    }

    /**
     * @param requestUrl
     * @param requestHeader
     * @param requestBody
     * @return
     */
    public static String invokePost(String requestUrl, Map<String, String> requestHeader, String requestBody) {
        return invokePost(requestUrl, null, requestHeader, requestBody, DEFAULT_ENCODING, DEFAULT_CONNECTION_TIME_OUT,
                DEFAULT_READ_TIME_OUT);
    }


    /**
     * @param requestUrl
     * @param params
     * @param requestHeader
     * @param requestBody
     * @param encode
     * @param connectTimeout
     * @param readTimeout
     * @return
     */
    public static String invokePost(String requestUrl, Map<String, Object> params, Map<String, String> requestHeader,
                                    String requestBody, String encode, int connectTimeout, int readTimeout) {
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeout)
                .setConnectTimeout(connectTimeout).setConnectionRequestTimeout(connectTimeout).build();
        String responseString;
        HttpPost httpPost = new HttpPost(requestUrl);
        httpPost.setConfig(requestConfig);

        if (MapUtils.isNotEmpty(requestHeader)) {
            for (Map.Entry<String, String> entry : requestHeader.entrySet()) {
                httpPost.addHeader(entry.getKey(), entry.getValue());
            }
        }

        buildPostParams(httpPost, params, requestBody, encode);
        responseString = doRequest(httpPost, encode);
        return responseString;
    }

    /**
     * @param httpPost
     * @param params
     * @param requestBody
     * @param encode
     */
    private static void buildPostParams(HttpPost httpPost, Map<String, Object> params, String requestBody,
                                        String encode) {
        try {
            if (MapUtils.isNotEmpty(params)) {
                List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();

                for (Map.Entry<String, Object> entry : params.entrySet()) {
                    String value = null;
                    if (entry.getValue() instanceof String) {
                        value = (String) entry.getValue();
                    } else {
                        value = JSON.toJSONString(entry.getValue());
                    }
                    nameValuePairs.add(new BasicNameValuePair(entry.getKey(), value));
                }

                httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, encode));
            }

            if (StringUtils.isNotBlank(requestBody)) {
                httpPost.setEntity(new StringEntity(requestBody, encode));
            }

        } catch (UnsupportedEncodingException e) {
            logger.error("HttpClientUtils.buildPostParams error, params = " + params, e);
        }
    }

    /**
     * @param httpRequestBase
     * @param encode
     * @return
     */
    private static String doRequest(HttpRequestBase httpRequestBase, String encode) {
        String responseString = null;
        try {
            long start = System.currentTimeMillis();
            CloseableHttpResponse response = httpClient.execute(httpRequestBase);
            logger.info("HttpClientUtils Begin Invoke: " + httpRequestBase.getURI() + ", cost time "
                    + (System.currentTimeMillis() - start) + " ms");
            try {
                Header[] headers = response.getHeaders("isLogin");
                if(headers!=null && headers.length>0){
                    custRespHeader.put(headers[0].getName(),headers[0].getValue());
                }
                HttpEntity entity = response.getEntity();
                try {
                    if (entity != null) {
                        responseString = EntityUtils.toString(entity, encode);
                    }
                } finally {
                    if (entity != null) {
                        entity.getContent().close();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                logger.error(String.format("[HttpClientUtils.doRequest] get response error, url:%s",
                        httpRequestBase.getURI()), e);
                responseString = "";
            } finally {
                if (response != null) {
                    response.close();
                }
            }
        } catch (SocketTimeoutException e) {
            e.printStackTrace();
            logger.error(String.format("[HttpClientUtils.doRequest] invoke get timout error, url:%s",
                    httpRequestBase.getURI()), e);
            responseString = "";
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(
                    String.format("[HttpClientUtils.doRequest] invoke get error, url:%s", httpRequestBase.getURI()), e);
            responseString = "";
        } finally {
            httpRequestBase.releaseConnection();
        }
        logger.info("HttpClientUtils response : " + responseString);

        return responseString;
    }

    /**
     * @param url
     * @param params
     * @return
     * @throws UnsupportedEncodingException
     */
    private static String buildRequestUrl(String url, Map<String, String> params) throws UnsupportedEncodingException {
        if (CollectionUtils.isEmpty(params)) {
            return url;
        }
        StringBuilder requestUrl = new StringBuilder();
        requestUrl.append(url);
        int i = 0;
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (i == 0) {
                requestUrl.append("?");
            }
            requestUrl.append(entry.getKey());
            requestUrl.append("=");
            String value = entry.getValue();
            requestUrl.append(URLEncoder.encode(value, "UTF-8"));
            requestUrl.append("&");
            i++;
        }
        requestUrl.deleteCharAt(requestUrl.length() - 1);
        return requestUrl.toString();
    }


    public static void setStringParams(HttpPost httpost,
                                       Map<String, String> params) {
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        Set<String> keySet = params.keySet();
        for (String key : keySet) {
            nvps.add(new BasicNameValuePair(key, params.get(key).toString()));
        }
        try {
            httpost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
        }
    }


    /**
     * @param httpRequest
     * @param entity
     * @param
     * @return
     */
    private static String sendRequest(HttpRequestBase httpRequest, HttpEntity entity) {

        httpRequest.setHeader("User-Agent", "okHttp");
        if (httpRequest instanceof HttpEntityEnclosingRequestBase) {
//	            checkArgument(null!=entity,"HttpEntity请求体不能为空");
            ((HttpEntityEnclosingRequestBase) httpRequest).setEntity(entity);
        }

        //忽略证书
        SSLContext sslContext = SSLContexts.createDefault();

        X509TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] xcs,
                                           String string) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] xcs,
                                           String string) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        try {
            sslContext.init(null, new TrustManager[]{tm}, null);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        X509HostnameVerifier hostnameVerifier = new AllowAllHostnameVerifier();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);

        Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("https", sslsf)
                .build();

        HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(r);
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();


        CloseableHttpResponse response = null;
        String resString = null;
        try {
            response = httpClient.execute(httpRequest);
            HttpEntity resEntity = response.getEntity();
            int statusCode = response.getStatusLine().getStatusCode();
            resString = EntityUtils.toString(resEntity, "UTF-8");
            System.err.println(statusCode);
            if (statusCode != HttpStatus.SC_OK) {
                System.out.println("响应码状态不是200");
            }
            return resString;
        } catch (Exception e) {
            throw new RuntimeException(resString, e);
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
                if (httpRequest != null) {
                    httpRequest.releaseConnection();
                }
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

}
